package com.goldsprite.gameframeworks.ecs.system;

import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.glutils.*;
import com.badlogic.gdx.utils.Pool;
import com.goldsprite.gameframeworks.ecs.LayerMask;
import com.goldsprite.gameframeworks.ecs.component.*;
import com.goldsprite.gameframeworks.log.Debug;
import com.goldsprite.gameframeworks.simplephys.collis.*;
import com.goldsprite.gameframeworks.simplephys.shape.*;
import com.goldsprite.gameframeworks.utils.StringUtils;
import com.goldsprite.gameframeworks.utils.math.Vector2;

import java.util.*;

import java.util.function.*;

@GameSystemInfo(
	type = GameSystemInfo.SystemType.BOTH,
	interestComponents = {RigidbodyComponent.class}
)
public class PhysicsSystem extends BaseSystem {
	public static final float EPS = 1e-4f;
	private int velIterations = 1;//速度迭代次数
	private int solverIterations = 1;//碰撞阻挡迭代次数

	private final Vector2 gravity = new Vector2(0, -9.8f * 1);
	private float dt;
	private boolean debugRenderEnabled;

	private Vector2 tmpVec2 = new Vector2();

	private Pool<FullCollisionResult> resultPool = new Pool<>() {
		public FullCollisionResult newObject() {
			return new FullCollisionResult();
		}
	};

	@Override
	public void update(float delta) {
		float accumulator = GameSystem.getFixedAccumulator();
		float alpha = accumulator / GameSystem.FIXED_DELTA_TIME;
		applyRenderPosition(/*alpha*/1);
	}

	public void applyRenderPosition(float alpha) {
		forEachRigi(rigi -> {
			rigi.getTransform().position.set(rigi.getPosition());
		});
	}

	@Override
	public void fixedUpdate(float fixedDelta) {
		dt = fixedDelta;
		// 完整的物理更新在一个固定步中完成
		updatePhysics(fixedDelta);
	}

	private void updatePhysics(float fixedDelta) {
		// 1. 重力更新（影响速度）
//		updateGravity(fixedDelta);

		// 3. 碰撞检测与响应
		handleCollisions(fixedDelta);
	}

	private void updateGravity(float fixedDelta) {
		forEachRigi(rigi -> {
			if (rigi.getBodyType() == BodyType.DYNAMIC) {
				// 重力影响速度（不是位置！）
				float rigiGravityScale = rigi.getGravityScale();
				Vector2 gDeltaVel = tmpVec2.set(gravity).scl(rigiGravityScale);
				rigi.addVelocity(gDeltaVel);
			}
		});
	}

	private void handleCollisions(float fixedDelta) {
		//获取碰撞信息
		detectAndCallback(velIterations, fixedDelta);

		//迭代式位置修正
		for (int i = 0; i < solverIterations; i++) {
			boolean moved = resolveCurrentContacts();
			//已无碰撞, 提前退出
			if (!moved) break;
		}
	}

	private void detectAndCallback(int velIterations, float fixedDelta) {
		float stepFixedDelta = fixedDelta / velIterations;
		for (int i = 0; i < velIterations; i++) {
			// 进入或持续回调
			forEachRigiComps(c1 -> {
				RigidbodyComponent rigi = c1.rigi;
				boolean isMovable = rigi.getBodyType() == BodyType.DYNAMIC || rigi.getBodyType() == BodyType.KINEMATIC;
				boolean isMoving = !rigi.getVelocity().isZero();
				if (!isMovable || !isMoving || !c1.isEnable()) return;

				forEachRigiComps(c2 -> {
					boolean bothTrigger = c1.isTrigger() && c2.isTrigger();
					if (c2.rigi == c1.rigi || !c2.isEnable() || bothTrigger) return;

					// 层掩码检查
					if (false && !canCollide(c1, c2)) return;

					//是否触发型接触
					boolean trigA = c1.isTrigger();
					boolean trigB = c2.isTrigger();
					boolean isTrigContact = trigA != trigB;

					//根据碰撞双方确定索引键, 并用其获取旧碰撞信息或创建新的
					long pairKey = getPairKey(c1, c2);
					boolean isNew = !contactInfos.containsKey(pairKey);
					FullCollisionResult result = contactInfos.getOrDefault(pairKey, resultPool.obtain());

					// 碰撞检测
					boolean isCollied = calculateCollision(fixedDelta, c1, c2, result, false);
					result.collided = isCollied;

					//如果新创建的碰撞信息没碰撞则回收, 然后跳过
					if (!isCollied) {
						if (isNew) resultPool.free(result);
						return;
					}

					//碰撞则录入此次碰撞信息
					result.self = c1;
					result.other = c2;
					result.isTrigContact = isTrigContact;
					result.isNew = isNew;
					contactInfos.put(pairKey, result);

					logColl(isNew ? 0 : 1, result);//调试信息

//					// 碰撞响应, 仅碰撞接触且深度超过0
					if (!isTrigContact)
						resolveCollision(c1, c2, result);

					// 碰撞回调
					{
						//新接触
						if (isNew) {
							// 触发器事件
							if (isTrigContact) {
								c1.onTriggerEnter(c2, result);
								c2.onTriggerEnter(c1, result);
							}
							// 碰撞事件
							else {
								c1.onCollisionEnter(c2, result);
								c2.onCollisionEnter(c1, result);
							}
						}
						//持续接触
						else {
							// 触发器事件
							if (isTrigContact) {
								c1.onTriggerStay(c2, result);
								c2.onTriggerStay(c1, result);
							}
							// 碰撞事件
							else {
								c1.onCollisionStay(c2, result);
								c2.onCollisionStay(c1, result);
							}
						}
					}
				});
			});
		}

		//退出回调
		iterationContacts(result -> {
			if (result.collided) return false;

			logColl(0, result);//调试日志
			//退出接触
			{
				ColliderComponent c1 = result.self;
				ColliderComponent c2 = result.other;
				boolean isTrigContact = result.isTrigContact;
				// 触发器事件
				if (isTrigContact) {
					c1.onTriggerExit(c2, result);
					c2.onTriggerExit(c1, result);
				}
				// 碰撞事件
				else {
					c1.onCollisionExit(c2, result);
					c2.onCollisionExit(c1, result);
				}
			}
			return true;
		});
	}

	private void iterationContacts(Predicate<FullCollisionResult> resultConsumer) {
		Iterator<Map.Entry<Long, FullCollisionResult>> iterator = contactInfos.entrySet().iterator();
		while (iterator.hasNext()) {
			Map.Entry<Long, FullCollisionResult> entry = iterator.next();
			FullCollisionResult result = entry.getValue();
			if (resultConsumer.test(result)) iterator.remove();
		}
	}

	private boolean resolveCurrentContacts() {

		//应用速度(如果碰撞则会提前应用, 且速度会被清除)
		forEachRigi(rigi -> {
			if (rigi.getBodyType().equals(BodyType.STATIC)) return;
			rigi.addPosition(rigi.getVelocity().scl(dt));
		});

		return false;
	}

	private boolean canCollide(ColliderComponent c1, ColliderComponent c2) {
		int l1 = c1.getLayer();
		int l2 = c2.getLayer();
		boolean boo = LayerMask.canLayersCollide(l1, l2);
		return boo;
	}

	private final Map<Long, FullCollisionResult> contactInfos = new HashMap<>();

	private long getPairKey(ColliderComponent a, ColliderComponent b) {
		// 保证a,b顺序一致：使用地址哈希
		int id1 = a.getRunnableGid();
		int id2 = b.getRunnableGid();
		//ab ba视为同样碰撞
		long minId = Math.min(id1, id2);
		long maxId = Math.max(id1, id2);
		return (minId << 16) | maxId;
//		//ab与ba 视为不同碰撞
//		return (id1 << 16) | id2;
	}

	private boolean calculateCollision(float fixedDelta, ColliderComponent c1, ColliderComponent c2, CollisionResult result, boolean isReset) {
		boolean collided = false;
		CollisionUtils.isResetCalc = isReset;

		if (c1.getShape() instanceof PhysRect r1 && c2.getShape() instanceof PhysRect r2) {
			r1.vel.scl(dt);
			r2.vel.scl(dt);
			//collided = CollisionUtils.rectRectDetector.preCollision(r1, r2);
			collided = CollisionUtils.rectRectDetector.ccdCollision3(r1, r2, result);
//			collided = CollisionUtils.rectRectDetector.ccdCollision(c1.rigi.getLastPosition(), (PhysRect) c1.getShape(), (PhysRect) c2.getShape(), result);
		}
//		else if (isCircle(c1) && isCircle(c2)) {
//			collided = CollisionUtils.calculateCollision((PhysCircle) c1.getShape(), (PhysCircle) c2.getShape(), result);
//		} else if (isCircle(c1) && isRect(c2)) {
//			collided = CollisionUtils.ccdCollision2(c1.rigi.getVelocity().scl(fixedDelta), (PhysCircle) c1.getShape(), (PhysRect) c2.getShape(), result);
////			collided = CollisionUtils.circleRectDetector.ccdCollision(c1.rigi.getLastPosition(), (PhysCircle) c1.getShape(), (PhysRect) c2.getShape(), result);
//		} else if (isRect(c1) && isCircle(c2)) {
//			collided = CollisionUtils.ccdCollision2(c1.rigi.getVelocity().scl(fixedDelta), (PhysRect) c1.getShape(), (PhysCircle) c2.getShape(), result);
//		}
		return collided;
	}

	private void resolveCollision(ColliderComponent c1, ColliderComponent c2, FullCollisionResult result) {
		// 只有碰撞器才进行阻挡
		RigidbodyComponent rigi1 = c1.rigi;
		RigidbodyComponent rigi2 = c2.rigi;
		float minT = result.t;

		if (result.isDcd) {
			if(c2.rigi.getVelocity().isZero()){
				Vector2 backDistance1 = tmpVec2.set(result.normal).scl(result.depth);
				rigi1.addPosition(backDistance1);
			}
			else {
				Debug.log("墙被玩家撞");
			}
		} else {
			float vx = dt * (result.normal.x == 0 ? 1 : minT);
			float vy = dt * (result.normal.y == 0 ? 1 : minT);
			rigi1.addPosition(rigi1.getVelocity().scl(vx, vy));

			// 可选：添加一个小偏移量防止卡住
			rigi1.addPosition(tmpVec2.set(result.normal).scl(EPS * 10));
		}

		Vector2 r1Vel = rigi1.getVelocity();
		rigi1.setVelocity(r1Vel.scl(r1Vel.x * result.normal.x < 0 ? 0 : 1, r1Vel.y * result.normal.y < 0 ? 0 : 1));
	}

	Vector2 tmpVec22 = new Vector2();

	public void applyPositionCorrection(RigidbodyComponent r1, RigidbodyComponent r2, CollisionResult result) {
		float r1Len = r1.getVelocity().len();
		float r2Len = r2.getVelocity().len();
		float len = r1Len + r2Len;
		float r1Weight = r1Len / len;
		float r2Weight = r2Len / len;
		r1Weight = r2Weight = 1;//暂时禁用权重, 皆为1

		Vector2 backDistance1 = tmpVec2.set(result.normal).scl(result.depth).scl(r1Weight);
//		r1.setLastPosition(r1.getPosition());//如果一个步长多次碰撞迭代这里不能更新否则会混乱
		r1.addPosition(backDistance1);

		Vector2 backDistance2 = tmpVec22.set(result.normal).scl(result.depth).scl(r2Weight);
//		r2.setLastPosition(r2.getPosition());
		r2.addPosition(backDistance2);

//		Debug.logT("NewCollision", "r1位置修正, 回退距离: %s /// r2位置修正, 回退距离: %s", backDistance1, backDistance2);
	}

	private void applyVelocityResponse(RigidbodyComponent rigi1, RigidbodyComponent r2, CollisionResult result) {
		float x = result.normal.x;
		float y = result.normal.y;
		Vector2 negaNormal = tmpVec2.set(x == 0 ? 1 : 0, y == 0 ? 1 : 0);
		rigi1.setVelocity(rigi1.getVelocity().scl(negaNormal));
	}

	public boolean isCircle(ColliderComponent c) {
		return c.getShapeType() == ShapeType.Circle;
	}

	public boolean isRect(ColliderComponent c) {
		return c.getShapeType() == ShapeType.Rect;
	}

	public void forEachRigi(Consumer<RigidbodyComponent> consume) {
		getInterestEntities().forEach(entity -> consume.accept(entity.getComponent(RigidbodyComponent.class)));
	}

	public void forEachRigiComps(Consumer<ColliderComponent> consume) {
		forEachRigi(rigi -> rigi.getAttachedColliders().forEach(consume));
	}

	private void logColl(int mode, FullCollisionResult result) {
		ColliderComponent c1 = result.self;
		ColliderComponent c2 = result.other;
		String state = result.isNew ? "首次" : "持续";
		if (!result.collided) state = "退出";
		String msg = StringUtils.formatString("%s%s接触, %s -> %s, result: %s", state, result.isTrigContact ? "Triggered" : "Collided", c1, c2, result);
		if (mode == 0) Debug.logT("NewCollision", msg);
		else Debug.infoT("NewCollision", msg);
	}

	public boolean isDebugRenderEnabled() {
		return debugRenderEnabled;
	}

	public void setDebugRenderEnabled(boolean debugRenderEnabled) {
		this.debugRenderEnabled = debugRenderEnabled;
	}

	public void renderDebug(ShapeRenderer shapeRenderer, OrthographicCamera worldCamera) {
	}
}
